package threads;

import java.util.Comparator;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 优先级参考 http://www.cnblogs.com/whoislcj/p/5610903.html
 */
public class PriorityThreadPool extends ThreadPoolExecutor {
    private static final int corePoolSize = 2 * Runtime.getRuntime().availableProcessors();
    private static PriorityThreadPool threadPool;
    private AtomicLong seqIndex = new AtomicLong(Long.MIN_VALUE);

    private PriorityThreadPool() {
        super(corePoolSize, corePoolSize, 30, TimeUnit.SECONDS,
//                new LinkedBlockingQueue<Runnable>(),
                new PriorityBlockingQueue<>(corePoolSize, FIFO),
                new CustomizedThreadFactory(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        //添加debug信息
                        Logger.logContent("thread " + r.getClass().getName() + " was rejected cause too may thread loaded");
                    }
                });
    }


    public static PriorityThreadPool getInstance() {
        if (threadPool == null) {
            synchronized (PriorityThreadPool.class) {
                if (threadPool == null) threadPool = new PriorityThreadPool();
            }
        }
        return threadPool;
    }

    /**
     * 线程队列方式 先进先出
     */
    private static final Comparator<Runnable> FIFO = new Comparator<Runnable>() {
        @Override
        public int compare(Runnable lhs, Runnable rhs) {
            if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) {
                PriorityRunnable lpr = ((PriorityRunnable) lhs);
                PriorityRunnable rpr = ((PriorityRunnable) rhs);
                int result = lpr.priority - rpr.priority;
                return result == 0 ? (int) (lpr.SEQ - rpr.SEQ) : result;
            } else {
                return 0;
            }
        }
    };

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                ((Future<?>) r).get();
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt(); // ignore/reset
            }
        }
        if (t != null) {
            Logger.logContent("Running task appeared exception! Thread [" + Thread.currentThread().getName() + "], because [" + t.getMessage() + "]\n");
        }
    }

    @Override
    public void execute(Runnable command) {
        if (command instanceof PriorityRunnable) {
            ((PriorityRunnable) command).SEQ = seqIndex.getAndIncrement();
        }
        super.execute(command);

    }


    public void execute(RunnablePriority priority, Runnable runnable) {
        if (runnable instanceof PriorityRunnable) {
            execute(runnable);
        } else {
            execute(new PriorityRunnable(runnable, priority));
        }
    }
}
